/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "local-include/reg.h"
#include <cpu/cpu.h>
#include <cpu/ifetch.h>
#include <cpu/decode.h>

#define R(i) gpr(i)
#define Mr vaddr_read
#define Mw vaddr_write
enum {
  TYPE_I, TYPE_U, TYPE_S,TYPE_R,TYPE_J,TYPE_B,
  TYPE_N, // none
};
#ifdef CONFIG_FTRACE
typedef struct func {
  word_t start;
  word_t size;
  word_t end;
  struct func *next;
  char  name [100];
} FC;
char * pace_deep=NULL;
char * pace_temp=NULL;
int deep_max = 0;
extern FC *fc_head;

  int func_deep = 0;
  char now_func [100] ;
  char  	peace[100];
//////////////////////////////////////////
//////// Function write_peace
//////////////////////////////////////////
void write_peace(){
    memset(peace, 0, sizeof(peace));
    strcpy(peace,"  ");
    for (size_t i = 0; i < func_deep; i++)
    {
        strcat(peace,"  ");
    }
}
//////////////////////////////////////////
//////////////////////////////////////////

//////////////////////////////////////////
//////// Function trace_func
//////////////////////////////////////////
void trace_func(uint64_t nxpc,uint64_t pc,uint32_t inst){
  FC*p = fc_head;
  while (p)
  {
    if ( nxpc == p->start)
    {
		
      if (inst == 0x00008067)
      {
        write_peace();
        printf("0x%lx:  %sreturn  [%s]\n",pc,peace,p->name);
        strcpy(now_func,p->name);
        func_deep = func_deep -1;
        break;
      }
      else{
        func_deep = func_deep +1;
        write_peace();
        printf("0x%lx:  %scall  [%s@0x%lx]\n",pc,peace,p->name,p->start);
        strcpy(now_func,p->name);
        break;
      }
    }
    else if (nxpc > p->start && nxpc <= p->end)
    {
      
      if (inst == 0x00008067)
      {
        write_peace();
        printf("0x%lx:  %sreturn  [%s]\n",pc,peace,p->name);
        strcpy(now_func,p->name);
        func_deep = func_deep -1;
        break;
      }
      else if (inst%0x80 == 0x6f || inst%0x80 == 0x67)
      {
        if (strcmp(now_func,p->name))
        {
          func_deep = func_deep +1;
          write_peace();
          printf("0x%lx:  %scall  [%s@0x%lx]\n",pc,peace,p->name,p->start);
          strcpy(now_func,p->name);
        }
        break;
      }
    }
    p=p->next;
  }
}
#endif

#define CSR_MTVEC   0x305
#define CSR_MCAUSE  0x342
#define CSR_MEPC    0x341
#define CSR_MSTATUS 0x300


#define src1R() do { *src1 = R(rs1); } while (0)
#define src2R() do { *src2 = R(rs2); } while (0)
#define immI() do { *imm = SEXT(BITS(i, 31, 20), 12); } while(0)
#define immU() do { *imm = SEXT(BITS(i, 31, 12), 20) << 12; } while(0)
#define immS() do { *imm = (SEXT(BITS(i, 31, 25), 7) << 5) | BITS(i, 11, 7); } while(0)
#define immJ() do { *imm = (SEXT(BITS(i, 31, 31), 1) << 20) |BITS(i, 19, 12) << 12|BITS(i, 20,20) << 11| BITS(i, 30,21)  << 1; } while(0)
#define immB() do { *imm = (SEXT(BITS(i, 31, 31), 1) << 12) |BITS(i, 7, 7) << 11 |BITS(i, 30,  25) << 5| BITS(i, 11, 8)<<1; } while(0)
static void decode_operand(Decode *s, int *dest, word_t *src1, word_t *src2, word_t *imm, int type) {
  uint32_t i = s->isa.inst.val;
  int rd  = BITS(i, 11, 7);
  int rs1 = BITS(i, 19, 15);
  int rs2 = BITS(i, 24, 20);
  *dest = rd;
  switch (type) {
    case TYPE_I: src1R();          immI(); break;
    case TYPE_U:                   immU(); break;
    case TYPE_S: src1R(); src2R(); immS(); break;
    case TYPE_J:                   immJ(); break;
    case TYPE_B: src1R(); src2R(); immB(); break;
    case TYPE_R: src1R(); src2R(); break;
  }
}
word_t  read_csr(word_t addr){
  uint32_t csr_addr = addr&0xfff;
  word_t dummy = 0;
  switch (csr_addr)
  {
    case CSR_MTVEC:return csr.mtvec ;
    case CSR_MCAUSE:return csr.mcause ;
    case CSR_MEPC:return csr.mepc ;
    case CSR_MSTATUS:return csr.mstatus ;
    default:return dummy; break;
  }
}
word_t mret(){
  csr.mstatus=(csr.mstatus&(~(word_t)((1<<3)|(1<<7))))|(word_t)(1<<7)|((word_t)((csr.mstatus>>7 & 1)<<3));
  return csr.mepc;
}
void write_csr(word_t addr,word_t data , int op){
  uint32_t csr_addr = addr&0xfff;
  word_t csr_data = read_csr(addr);
  switch (op)
  {
    case 1: csr_data  =  data; break;
    case 2: csr_data |=  data; break;
    case 3: csr_data &=  ~data;break;
    default:assert(1);
      break;
  }
  switch (csr_addr)
  { 
    case CSR_MTVEC:   csr.mtvec   = csr_data;break;
    case CSR_MCAUSE:  csr.mcause  = csr_data;break;
    case CSR_MEPC:    csr.mepc    = csr_data;break;
    case CSR_MSTATUS: csr.mstatus = csr_data;break;
    default:assert(1);
      break;
  }
}
static int decode_exec(Decode *s) {
  int dest = 0;
  word_t src1 = 0, src2 = 0, imm = 0;
  s->dnpc = s->snpc;
#define INSTPAT_INST(s) ((s)->isa.inst.val)
#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \
  decode_operand(s, &dest, &src1, &src2, &imm, concat(TYPE_, type)); \
  __VA_ARGS__ ; \
}


#define  zimm ((uint64_t)((uint32_t)BITS(s->isa.inst.val,19,15)))
//addi jal 1101111  mv 0010011
  INSTPAT_START(); 
//TYPE_I
  INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi   , I, R(dest) = (src1 + imm));
  INSTPAT("000000? ????? ????? 001 ????? 00100 11", slli   , I, R(dest) = src1 << BITS(imm, 5, 0));
  INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti   , I, R(dest) = ((int64_t)src1 < (int64_t)imm)?1:0);
  INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu  , I, R(dest) = (src1 < imm)?1:0);
  INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori   , I, R(dest) = src1 ^ (int)imm);
  INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori    , I, R(dest) = src1 | (int)imm);
  INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi   , I, R(dest) = src1 & (int)imm);
  INSTPAT("000000? ????? ????? 101 ????? 00100 11", srli   , I, R(dest) = src1 >> BITS(imm, 5, 0));
  INSTPAT("010000? ????? ????? 101 ????? 00100 11", srai   , I, R(dest) = (long int)src1 >> BITS(imm, 5, 0));
  

//TYPE_R

  INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add    , R, R(dest) = (src1 + src2));
  INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub    , R, R(dest) = (src1 - src2));
  INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll    , R, R(dest) = src1 << src2);
  INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt    , R, R(dest) = ((int64_t)src1 < (int64_t)src2)?1:0);
  INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu   , R, R(dest) = (src1 < src2)?1:0);
  INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl    , R, R(dest) = src1 >> src2);
  INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra    , R, R(dest) = (long int)src1 >> src2);
  INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor    , R, R(dest) = src1 ^ src2);
  INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or     , R, R(dest) = src1 | src2);
  INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and    , R, R(dest) = src1 & src2);
//TYPE_U
  INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc  , U, R(dest) = s->pc + imm);
  INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui    , U, R(dest) = imm);
//TYPE_L
  INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb     , I, R(dest) = SEXT(Mr(src1 + imm, 1),8));  
  INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh     , I, R(dest) = SEXT(Mr(src1 + imm, 2),16));  
  INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw     , I, R(dest) = SEXT(Mr(src1 + imm, 4),32));  
  INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu    , I, R(dest) = Mr(src1 + imm, 1)); 
  INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu    , I, R(dest) = Mr(src1 + imm, 2)); 
  INSTPAT("??????? ????? ????? 011 ????? 00000 11", ld     , I, R(dest) = Mr(src1 + imm, 8));
//TYPE_S
  INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb     , S, Mw(src1 + imm, 1, src2));
  INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh     , S, Mw(src1 + imm, 2, src2));
  INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw     , S, Mw(src1 + imm, 4, src2));
  INSTPAT("??????? ????? ????? 011 ????? 01000 11", sd     , S, Mw(src1 + imm, 8, src2));

  //TYPE_J
  INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal    , J, R(dest) = s->pc+4 ,s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr   , I, R(dest) = s->pc+4 ,s->dnpc = src1 + imm);
  //TYEP_B
  INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq    , B, if(src1==src2)s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne    , B, if(src1!=src2)s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt    , B, if((int64_t)src1<(int64_t)src2)s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge    , B, if((int64_t)src1>=(int64_t)src2)s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu   , B, if(src1<src2)s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu   , B, if(src1>=src2)s->dnpc = s->pc + imm);

  //RV64I

  INSTPAT("??????? ????? ????? 110 ????? 00000 11", lwu    , I, R(dest) = (uint32_t)Mr((src1 +imm), 4));

  INSTPAT("0000000 ????? ????? 001 ????? 01110 11", sllw   , R, R(dest) = SEXT((uint32_t)src1 << src2,32));
  INSTPAT("0000000 ????? ????? 101 ????? 01110 11", srlw   , R, R(dest) = SEXT((uint32_t)src1 >> src2,32));
  INSTPAT("0100000 ????? ????? 101 ????? 01110 11", sraw   , R, R(dest) = SEXT((int)src1 >> src2,32));

  INSTPAT("0000000 ????? ????? 001 ????? 00110 11", slliw  , I, R(dest) = SEXT((uint32_t)src1 << BITS(imm, 4, 0),32));
  INSTPAT("0000000 ????? ????? 101 ????? 00110 11", srliw  , I, R(dest) = SEXT((uint32_t)src1 >> BITS(imm, 4, 0),32));
  INSTPAT("0100000 ????? ????? 101 ????? 00110 11", sraiw  , I, R(dest) = SEXT((int)src1 >> BITS(imm, 4, 0),32));

  INSTPAT("0000000 ????? ????? 000 ????? 01110 11", addw   , R, R(dest) = SEXT((int)(src1 + src2),32));
  INSTPAT("??????? ????? ????? 000 ????? 00110 11", addiw  , I, R(dest) = SEXT((int)(src1 + imm),32));
  INSTPAT("0100000 ????? ????? 000 ????? 01110 11", subw   , R, R(dest) = SEXT((int)(src1 - src2),32));



  INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul    , R, R(dest) = src1 * src2);
  INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh    ,R, R(dest) = ((__int128_t)src1 * (__int128_t) src2)>>64);
  INSTPAT("0000001 ????? ????? 010 ????? 01100 11", mulhsu  ,R, R(dest) = ((__int128_t)src1 * (__uint128_t) src2)>>64);
  INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu   ,R, R(dest) = ((__uint128_t)src1*(__uint128_t)src2)>>64);
  INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div    , R, R(dest) = (int64_t)src1  / (int64_t)src2);
  INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu   , R, R(dest) = src1  / src2);
  INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem    , R, R(dest) = (int64_t)src1  % (int64_t)src2);
  INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu   , R, R(dest) = src1  % src2);
  INSTPAT("0000001 ????? ????? 000 ????? 01110 11", mulw   , R, R(dest) = SEXT(BITS((int)src1 * (int)src2, 31, 0),32));
  INSTPAT("0000001 ????? ????? 100 ????? 01110 11", divw   , R, R(dest) = SEXT((int)src1 / (int)src2,32));
  INSTPAT("0000001 ????? ????? 101 ????? 01110 11", divuw  , R, R(dest) = SEXT((uint32_t)src1  / (uint32_t)src2,32));
  INSTPAT("0000001 ????? ????? 110 ????? 01110 11", remw   , R, R(dest) = SEXT((int)src1  % (int)src2,32));
  INSTPAT("0000001 ????? ????? 111 ????? 01110 11", remuw  , R, R(dest) = SEXT((uint32_t)src1  % (uint32_t)src2,32));

  INSTPAT("??????? ????? ????? 001 ????? 11100 11", csrrw  , I, R(dest) = read_csr(imm),write_csr(imm,src1,1) );
  INSTPAT("??????? ????? ????? 010 ????? 11100 11", csrrs  , I, R(dest) = read_csr(imm),write_csr(imm,src1,2));
  INSTPAT("??????? ????? ????? 011 ????? 11100 11", csrrc  , I, R(dest) = read_csr(imm),write_csr(imm,src1,3));
  INSTPAT("??????? ????? ????? 101 ????? 11100 11", csrrwi , I, R(dest) = read_csr(imm),write_csr(imm,zimm,1));
  INSTPAT("??????? ????? ????? 110 ????? 11100 11", csrrsi , I, R(dest) = read_csr(imm),write_csr(imm,zimm,2));
  INSTPAT("??????? ????? ????? 111 ????? 11100 11", csrrci , I, R(dest) = read_csr(imm),write_csr(imm,zimm,3));
  INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0 
  INSTPAT("0000000 00000 00000 000 00000 11100 11", ecall  , N, s->dnpc=isa_raise_intr(11,s->pc)); // R(10) is $a0 
  INSTPAT("0011000 00010 00000 000 00000 11100 11", mret   , N, s->dnpc=mret());
  INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv    , N, INV(s->pc));
  INSTPAT_END();

  R(0) = 0; // reset $zero to 0
#ifdef CONFIG_FTRACE
  if (s->isa.inst.val%0x80 == 0x6f || s->isa.inst.val%0x80 == 0x67 || s->isa.inst.val%0x80 == 0x63)
  {
    trace_func(s->dnpc,s->pc,s->isa.inst.val);
   
  }
    if (deep_max < func_deep)
  {
    deep_max = func_deep;
  }
#endif
#ifdef CONFIG_ETRACE
  if (s->isa.inst.val%0x7F == 0x73)
  {
    printf("pc= 0x%lx \t mepc = 0x%lx \t mstatus = 0x%lx \t mcause = 0x%lx \n",s->pc,csr.mepc,csr.mstatus,csr.mcause);
   
  }
#endif
  // if (s->pc == 0x80000090)
  // {
  //   printf("a0 = 0x%lx   a5 = 0x%lx\n",R(10),R(15));
  // }
  
  return 0;
}

int isa_exec_once(Decode *s) {
  s->isa.inst.val = inst_fetch(&s->snpc, 4);
  // FC * p = NULL;
  // p = fc_head;
  // while (p)
  // {

  //   printf("0x%lx,0x%lx,0x%lx,%s\n",p->start,p->size,p->end,p->name);
  //   p = p->next;
  // }
  return decode_exec(s);
}
